package com.example.demo.nio;


import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

public class NioServer {

    public static void main(String[] args) throws IOException {
        // 获取一个ServerSocketChannel对象,用来表示服务端通道。这个通道的作用就是让服务端能和客户端通道建立连接
        ServerSocketChannel ssc = ServerSocketChannel.open();
        // 设置这个通道为非阻塞模式
        ssc.configureBlocking(false);
        // 设置服务端程序端口号
        ssc.socket().bind(new InetSocketAddress(7000));
        // 获取多路复用器
        Selector selector= Selector.open();
        // 给服务端通道注册一个客户端连接事件的监听
        ssc.register(selector, SelectionKey.OP_ACCEPT);

        while(true){
            // 复用器等待新事件注册(阻塞等待),如果监听到则获取当前所有待执行的事件
            selector.select();
            // 获取待执行事件集合
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            // 将集合交给迭代器
            Iterator<SelectionKey> iterator =selectionKeys.iterator();
            // 迭代器遍历
            while(iterator.hasNext()){
                SelectionKey selectionKey= iterator.next();
                // 将事件逐一交给handle处理
                handle(selectionKey);
                iterator.remove();
            }
        }
    }

    private static void handle(SelectionKey selectionKey) throws IOException {
        // 判断本次要执行的事件
        if(selectionKey.isAcceptable()){
            // 将selectionKey转换成服务端通道对象
            ServerSocketChannel channel = (ServerSocketChannel)selectionKey.channel();
            // 通过服务端通道使客户端通道成功的和服务端建立一个通道SocketChannel
            SocketChannel socketChannel = channel.accept();
            // 设置为非阻塞
            socketChannel.configureBlocking(false);
            // 给这个通道注册一个读取事件(现在主体是服务端，所以这个读取就表示等待客户端的数据传输过来)。
            // 如果有写出数据给客户端的话，也可以在这里一起注册好。如果有什么特殊情况也可以在其他地方随时注册。
            socketChannel.register(selectionKey.selector(),SelectionKey.OP_READ);

        }else if(selectionKey.isReadable()){
            // 因为是读取事件，所以需要转为通道SocketChannel了
            SocketChannel socketChannel = (SocketChannel)selectionKey.channel();
            // 用一个缓冲区来接数据
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            // 将数据读进缓冲区中
            int len =socketChannel.read(buffer);
            if(len!=-1){
                // 如果数据正常则输出看看
                System.out.println(new String(buffer.array(),0,len));
            }
            // 给客户端返回一些数据
            ByteBuffer byteBuffer=ByteBuffer.wrap("hello".getBytes());
            // 让通道对象去操作写入
            socketChannel.write(byteBuffer);

        }


    }


}
